`timescale 1ns / 1ps
/******************************************************************************
*                                                                             *
* UTICA softcore v0.1                                                         *
*                                                                             *
* Copyright (c) 2012 Andrew D. Zonenberg                                      *
* All rights reserved.                                                        *
*                                                                             *
* Redistribution and use in source and binary forms, with or without modifi-  *
* cation, are permitted provided that the following conditions are met:       *
*                                                                             *
*    * Redistributions of source code must retain the above copyright notice  *
*      this list of conditions and the following disclaimer.                  *
*                                                                             *
*    * Redistributions in binary form must reproduce the above copyright      *
*      notice, this list of conditions and the following disclaimer in the    *
*      documentation and/or other materials provided with the distribution.   *
*                                                                             *
*    * Neither the name of the author nor the names of any contributors may be*
*      used to endorse or promote products derived from this software without *
*      specific prior written permission.                                     *
*                                                                             *
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF        *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN     *
* NO EVENT SHALL THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT,         *
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT    *
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,   *
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY       *
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT         *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF    *
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.           *
*                                                                             *
******************************************************************************/

/**
	@file HardwareTestbench_SpiNorFlashController.v
	@author Andrew D. Zonenberg
	@brief Top-level module for testing SPI flash
 */
module HardwareTestbench_SpiNorFlashController(
	clk_20mhz,
	spi_sck, spi_cs_n, spi_data,
	leds, buttons,
	uart_tx, uart_rx
    );
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// IO declarations
	input wire clk_20mhz;
	
	output wire spi_sck;
	output wire spi_cs_n;
	inout wire[3:0] spi_data;
	
	output reg[7:0] leds = 0;
	
	input wire[3:0] buttons;
	
	input wire uart_rx;
	output wire uart_tx;
	 
	////////////////////////////////////////////////////////////////////////////////////////////////
	// Frequency synthesis for clock
	
	wire clk_fb;
	wire sysclk;
	wire sysclk_n;
	DCM_SP #(
		.CLKDV_DIVIDE(2.0),
		.CLKFX_DIVIDE(1),
		.CLKFX_MULTIPLY(4),						//80 MHz
		.CLKIN_DIVIDE_BY_2("FALSE"),
		.CLKIN_PERIOD(50.0),						//20 MHz input
		.CLKOUT_PHASE_SHIFT("NONE"),
		.CLK_FEEDBACK("1X"),
		.DESKEW_ADJUST("SYSTEM_SYNCHRONOUS"),
		.DFS_FREQUENCY_MODE("LOW"),
		.DLL_FREQUENCY_MODE("LOW"),
		.DSS_MODE("NONE"),
		.DUTY_CYCLE_CORRECTION("TRUE"),
		.FACTORY_JF(16'hc080),
		.PHASE_SHIFT(0),
		.STARTUP_WAIT("FALSE")
	)
	clkmgr (
		.CLK0(clk_fb),
		//.CLK180(CLK180),
		//.CLK270(CLK270),
		//.CLK2X(CLK2X),
		//.CLK2X180(CLK2X180),
		//.CLK90(CLK90),
		//.CLKDV(CLKDV),
		.CLKFX(sysclk),
		.CLKFX180(sysclk_n),
		//.LOCKED(LOCKED),
		//.PSDONE(PSDONE),
		//.STATUS(STATUS),
		.CLKFB(clk_fb),
		.CLKIN(clk_20mhz),
		.DSSEN(1'b0),
		.PSCLK(1'b0),
		.PSEN(1'b0),
		.PSINCDEC(1'b0),
		.RST(1'b0)
	);
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// Generate a nice slow debouncing clock
	reg clk_slow_edge = 0;							//asserted for one clk cycle every 2^16 cycles (roughly 1 KHz at 80 MHz)
	reg[15:0] clkdiv = 0;
	always @(posedge sysclk) begin
		clkdiv <= clkdiv + 1;
		clk_slow_edge <= 0;
		if(clkdiv == 0)
			clk_slow_edge <= 1;
	end
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// Debounce the buttons
	
	wire[3:0] buttons_debounced;
	
	SwitchDebouncer btn0 (.clk(sysclk), .clken(clk_slow_edge), .din(buttons[0]), .dout(buttons_debounced[0]));
	SwitchDebouncer btn1 (.clk(sysclk), .clken(clk_slow_edge), .din(buttons[1]), .dout(buttons_debounced[1]));
	SwitchDebouncer btn2 (.clk(sysclk), .clken(clk_slow_edge), .din(buttons[2]), .dout(buttons_debounced[2]));
	SwitchDebouncer btn3 (.clk(sysclk), .clken(clk_slow_edge), .din(buttons[3]), .dout(buttons_debounced[3]));
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// Flash controller
	
	reg cmd_en = 0;
	reg[3:0] cmd_din = 0;
	reg read_en = 0;
	wire[7:0] read_data;
	wire read_overflow;
	wire read_underflow;
	wire read_full;
	wire read_empty;
	wire busy;
	reg read_en_buf = 0;
	reg[23:0] cmd_addr = 0;
	reg[7:0] cmd_burstlen = 0;
	SpiNorFlashController controller (
		.clk(sysclk), 
		.busy(busy), 
		.spi_sck(spi_sck), 
		.spi_cs_n(spi_cs_n), 
		.spi_data(spi_data), 
		.cmd_en(cmd_en), 
		.cmd_din(cmd_din), 
		.cmd_addr(cmd_addr), 
		.cmd_burstlen(cmd_burstlen), 
		.read_en(read_en), 
		.read_data(read_data), 
		.read_overflow(read_overflow), 
		.read_underflow(read_underflow), 
		.read_full(read_full), 
		.read_empty(read_empty)
		);
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// Serial interface for printing debug info
	wire console_busy;
	reg console_space_en = 0;
	reg console_newline_en = 0;
	HexUartPrinter console (
		.clk(sysclk), 
		.busy(console_busy), 
		.uart_tx(uart_tx), 
		.uart_rx(uart_rx), 
		.tx_data(read_data), 
		.tx_en(read_en_buf), 
		.space_en(console_space_en), 
		.newline_en(console_newline_en)
		);

	////////////////////////////////////////////////////////////////////////////////////////////////
	// State machine for test logic

	reg[3:0] buttons_buf = 0;

	reg[24:0] reset_count = 0;
	reg[15:0] state = 0;
	reg[9:0] tcount = 10'h1;
	always @(posedge sysclk) begin
	
		cmd_en <= 0;
		cmd_din <= 0;
		read_en <= 0;
		read_en_buf <= read_en;
		console_newline_en <= 0;
		
		buttons_buf <= buttons_debounced;

		case(state)
			
			//Wait >= 10us for flash to power up before doing anything
			//At 80 MHz this is 800 clocks. We'll use a 10-bit counter (1024 clocks) just to keep things simple.
			0: begin
				tcount <= tcount + 10'd1;
				if(tcount == 0)
					state <= 1;
			end
			
			//Request a read of the vendor ID
			1: begin
				cmd_en <= 1;
				cmd_din <= 4'b0001;	//CMD_READ_JEDEC_ID
				state <= 2;
				tcount <= 0;
				leds[7] <= 1;			//Say "we're alive"
			end
			
			//Read three bytes of data
			2: begin
				
				//Data available? Request a read (but not two in a row)
				//Furthermore, do not try to read if the UART is busy.
				if(!read_empty && !read_en && !console_busy && !read_en_buf) begin
					read_en <= 1;
					tcount <= tcount + 10'd1;
				end
				
				//Read data is on the bus - validate it
				if(read_en_buf) begin
					
					//Winbond W25Q80BV should be EF 40 14
					case(tcount)
						
						1: begin
							if(read_data == 8'hEF)
								leds[0] <= 1;
							else
								leds[6] <= 1;	//error
						end
						
						2: begin
							if(read_data == 8'h40)
								leds[1] <= 1;
							else
								leds[6] <= 1;	//error
						end
						
						3: begin
						
							if(read_data == 8'h14)
								leds[2] <= 1;
							else
								leds[6] <= 1;	//error
						
							state <= 3;
						end
						
					endcase
					
				end
				
				
			end
			
			//print newline
			3: begin
				if(!console_busy) begin
					console_newline_en <= 1;
					state <= 4;
				end
			end
			
			//Read unique ID from the flash device
			4: begin
				cmd_en <= 1;
				cmd_din <= 4'b0010;	//CMD_READ_UUID
				state <= 5;
				tcount <= 0;
			end
			
			//Read eight ID bytes and print to UART
			5: begin
				//Data available? Request a read (but not two in a row)
				if(!read_empty && !read_en && !console_busy && !read_en_buf) begin
					read_en <= 1;
					tcount <= tcount + 10'd1;
				end
				
				//Read data is on the bus - do something with it
				if(read_en_buf) begin
				
					//Quit when we're done
					if(tcount == 8)
						state <= 6;
				end
				
			end
			
			//print newline
			6: begin
				if(!console_busy) begin
					console_newline_en <= 1;
					state <= 7;
				end
			end
			
			//Read first 32 bytes of the flash
			7: begin
				cmd_en <= 1;
				cmd_din <= 4'b0011;			//CMD_READ_DATA
				cmd_addr <= 24'h000010;		//first 16 bytes of flash are blank
				cmd_burstlen <= 16;
				state <= 8;
				tcount <= 0;
			end
			
			//Read data and print to UART
			8: begin
				//Data available? Request a read (but not two in a row)
				if(!read_empty && !read_en && !console_busy && !read_en_buf) begin
					read_en <= 1;
					tcount <= tcount + 10'd1;
				end
				
				//Read data is on the bus - do something with it
				if(read_en_buf) begin
				
					//Quit when we're done
					if(tcount == 16)
						state <= 9;
				end
				
			end
			
			//print newline
			9: begin
				if(!console_busy) begin
					console_newline_en <= 1;
					state <= 10;
				end
			end
			
			//TODO: more tests here
			
			//End of test logic
			//Spin in an idle loop until button 0 is pressed
			10: begin	
				if(!buttons_buf[0] && buttons_debounced[0]) begin
					reset_count <= 1;
					state <=  10;
					leds <= 1;
				end
			end
			
			11: begin
				reset_count <= reset_count + 1;
				if(reset_count == 0)
					state <= 1;
			end
			
		endcase
	end


endmodule
